7. 도커 개요5 (레이어 구조 실습)

실습 개요

실습 진행 전 안내:

이 실습은 본인의 실습 디렉토리에서 진행

아래 예시 경로는 참고용이며, 본인 환경에 맞게 수정하여 진행

⚠️ Windows 사용자 :

C 드라이브 사용 권장 - Docker Desktop은 기본적으로 C 드라이브에 대한 파일 공유 권한을 가지고 있음

다른 드라이브 사용 시 권한 설정 필요:

  1. Docker Desktop 설정 → Resources → File Sharing
  2. 사용할 드라이브 또는 디렉토리 추가
  3. Apply & Restart 클릭

권한 문제 증상:

권장 사항: 실습 중 권한 문제가 발생하면 C:\docker-practice로 변경

이 문서에서는 컨테이너 레이어 구조를 직접 확인하는 실습을 진행. 총 3개의 실습으로 구성:

실습 구성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[실습 준비] 이미지 확인 및 빌드
    ↓
[실습 1] docker save로 이미지 내부 분석
    ↓
[실습 2] 오버레이 파일시스템 직접 체험
    ↓
[실습 3] Docker 스토리지 위치 확인 (선택)

실습 환경:


실습 준비: myimage:v1 이미지 확인 및 빌드

1단계: 이미지 존재 여부 확인

모든 OS 공통 (PowerShell/Bash):

# 이미지 목록 확인
docker images myimage

# 출력 예시 1 - 이미지가 있는 경우:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
# myimage      v1        a1b2c3d4e5f6   2 hours ago     77.8MB

# 출력 예시 2 - 이미지가 없는 경우:
# REPOSITORY   TAG       IMAGE ID       CREATED         SIZE

결과 판단:


2단계: myimage:v1 이미지 빌드 (이미지가 없는 경우)

2-1. 실습 디렉터리로 이동 및 확인

PowerShell (Windows):

# 실습 디렉터리로 이동 (본인 경로로 변경)
cd C:\docker-practice

# hello 디렉터리 확인 (이미 있는지 확인)
ls hello

# 출력 예시:
#     디렉터리: C:\docker-practice\hello
#
# Mode                 LastWriteTime         Length Name
# ----                 -------------         ------ ----
# -a----        2026-01-08   오전 10:30            100 hello.sh
# -a----        2026-01-08   오전 10:30            150 Dockerfile

Bash (Linux/Mac):

# 실습 디렉터리로 이동 (본인 경로로 변경)
cd ~/docker-practice

# hello 디렉터리 확인 (이미 있는지 확인)
ls -la hello/

# 출력 예시:
# total 8
# -rw-r--r-- 1 user user 100 Jan  8 10:30 hello.sh
# -rw-r--r-- 1 user user 150 Jan  8 10:30 Dockerfile

만약 hello 디렉터리가 없다면 새로 생성:

PowerShell (Windows):

# hello 디렉터리 생성
mkdir hello
cd hello

# hello.sh 스크립트 작성
@"
#!/bin/bash
set -eu
echo "Hello, World!"
exec sleep infinity
"@ | Set-Content hello.sh -NoNewline

# Dockerfile 작성
@"
FROM ubuntu:22.04
COPY ./hello.sh /hello.sh
RUN chmod +x /hello.sh
ENTRYPOINT ["/hello.sh"]
"@ | Set-Content Dockerfile -NoNewline

# 상위 디렉터리로 이동
cd ..

Bash (Linux/Mac):

# hello 디렉터리 생성
mkdir -p hello
cd hello

# hello.sh 스크립트 작성
cat > hello.sh << 'EOF'
#!/bin/bash
set -eu
echo "Hello, World!"
exec sleep infinity
EOF

# Dockerfile 작성
cat > Dockerfile << 'EOF'
FROM ubuntu:22.04
COPY ./hello.sh /hello.sh
RUN chmod +x /hello.sh
ENTRYPOINT ["/hello.sh"]
EOF

# 상위 디렉터리로 이동
cd ..

2-2. 이미지 빌드 실행

모든 OS 공통 (PowerShell/Bash):

# hello 디렉터리를 컨텍스트로 이미지 빌드
docker build -t myimage:v1 ./hello

빌드 과정 출력 예시:

[+] Building 12.3s (8/8) FINISHED
 => [internal] load build definition from Dockerfile                      0.1s
 => => transferring dockerfile: 150B                                      0.0s
 => [internal] load .dockerignore                                         0.0s
 => [internal] load metadata for docker.io/library/ubuntu:22.04           1.2s
 => [1/3] FROM docker.io/library/ubuntu:22.04@sha256:...                  5.4s
 => => resolve docker.io/library/ubuntu:22.04@sha256:...                  0.0s
 => => sha256:... 29.53MB / 29.53MB                                       4.2s
 => => extracting sha256:...                                              1.1s
 => [internal] load build context                                         0.1s
 => => transferring context: 100B                                         0.0s
 => [2/3] COPY ./hello.sh /hello.sh                                       0.2s
 => [3/3] RUN chmod +x /hello.sh                                          0.8s
 => exporting to image                                                    0.3s
 => => exporting layers                                                   0.2s
 => => writing image sha256:a1b2c3d4e5f6...                               0.0s
 => => naming to docker.io/library/myimage:v1                             0.0s

2-3. 빌드 완료 확인

모든 OS 공통 (PowerShell/Bash):

# 이미지 생성 확인
docker images myimage

# 출력:
# REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
# myimage      v1        a1b2c3d4e5f6   10 seconds ago   77.8MB

실습 1: 컨테이너 이미지 내부 분석

실습 목표

docker save 명령어로 myimage:v1 이미지를 tar 형식으로 추출하고 내부 파일 구조를 분석하여 레이어 구조를 확인

용어 정리

docker save: 이미지를 tar 파일로 추출하는 명령어. 레이어 구조 분석, 백업, 오프라인 전송에 사용됨.

tar: Tape Archive의 약자. 여러 파일/디렉터리를 하나로 묶는 아카이브 형식.

실습 환경


1단계: 이미지 추출용 디렉터리 생성

PowerShell (Windows):

# 실습 디렉터리로 이동 (본인 경로로 변경)
cd C:\docker-practice

# 이미지 추출용 디렉터리 생성
mkdir dumpimage

# 결과 확인
ls dumpimage

# 출력: (빈 디렉터리)

Bash (Linux/Mac):

# 실습 디렉터리로 이동 (본인 경로로 변경)
cd ~/docker-practice

# 이미지 추출용 디렉터리 생성
mkdir -p dumpimage

# 결과 확인
ls -la dumpimage/

# 출력: (빈 디렉터리)

2단계: docker save로 이미지 추출

PowerShell (Windows):

⚠️ 중요: Windows PowerShell에서 docker save | tar 형태의 파이프는 바이너리 데이터 손상 문제가 발생. 반드시 중간 파일을 생성하는 방식을 사용

# ❌ 작동하지 않음 (PowerShell 파이프 문제)
# docker save myimage:v1 | tar -xC ./dumpimage

# ✅ 올바른 방법: 중간 파일 생성
# 1. 이미지를 tar 파일로 저장
docker save myimage:v1 -o myimage.tar

# 2. tar 파일 압축 해제
tar -xf myimage.tar -C ./dumpimage

# 3. 임시 tar 파일 삭제 (선택)
rm myimage.tar

Bash (Linux/Mac):

# 방법 1: 파이프 사용 (권장)
docker save myimage:v1 | tar -xC ./dumpimage

# 방법 2: 중간 파일 생성
docker save myimage:v1 -o myimage.tar
tar -xf myimage.tar -C ./dumpimage
rm myimage.tar

처리 시간: 약 2-5초 (이미지 크기에 따라 다름)


3단계: 추출된 파일 구조 확인

PowerShell (Windows):

# 디렉터리 구조 확인
ls ./dumpimage/

출력 (OCI 형식):

    디렉터리: C:\docker-practice\dumpimage

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----      2026-01-05  오후 10:14                blobs
-a----      2026-01-08  오후 11:24            355 index.json
-a----      1970-01-01   오전 9:00           1371 manifest.json
-a----      1970-01-01   오전 9:00             31 oci-layout
-a----      1970-01-01   오전 9:00             86 repositories

Bash (Linux/Mac):

# 디렉터리 구조 확인
ls -la ./dumpimage/

출력 (OCI 형식):

total 24
drwxr-xr-x 3 user user 4096 Jan  8 23:24 .
drwxr-xr-x 5 user user 4096 Jan  8 23:20 ..
drwxr-xr-x 3 user user 4096 Jan  5 22:14 blobs
-rw-r--r-- 1 user user  355 Jan  8 23:24 index.json
-rw-r--r-- 1 user user 1371 Jan  1  1970 manifest.json
-rw-r--r-- 1 user user   31 Jan  1  1970 oci-layout
-rw-r--r-- 1 user user   86 Jan  1  1970 repositories

📌 참고: OCI vs Moby 형식

최신 Docker는 OCI(Open Container Initiative) 형식으로 저장하며, 레이어 파일들이 blobs/sha256/ 디렉터리에 저장. 구조는 다르지만 레이어 개념은 동일

용어 정리

OCI (Open Container Initiative): 컨테이너 표준을 정의하는 단체. 이미지 형식, 런타임 규격 표준화를 담당함.

SHA256: 256비트 해시 함수. 레이어, 이미지 등을 고유하게 식별하는 데 사용됨.

파일 설명:

파일/디렉터리 역할 내용
blobs/sha256/ 레이어 저장소 모든 레이어 tar 파일이 SHA256 해시명으로 저장됨
manifest.json 이미지 구성 정보 레이어 순서, Config 파일 위치, 태그
index.json OCI 인덱스 manifest 위치 정보
repositories 태그 정보 이미지 이름 및 태그
oci-layout OCI 규격 버전 이미지 포맷 버전

4단계: manifest.json 내용 확인

# manifest.json 읽기
cat ./dumpimage/manifest.json | ConvertFrom-Json | ConvertTo-Json -Depth 5

출력 예시 (간소화):

[
  {
    "Config": "2c3d4e5f6a7b8901...json",
    "RepoTags": [
      "myimage:v1"
    ],
    "Layers": [
      "0a1b2c3d4e5f6789.../layer.tar",
      "1b2c3d4e5f6a7890.../layer.tar",
      "2c3d4e5f6a7b8901.../layer.tar"
    ]
  }
]

분석:


5단계: 특정 레이어 내용 확인

5-1. layer.tar 파일 목록 확인

# 레이어 2 (hello.sh 추가 레이어) 확인 - 실제 해시값 사용
# 예시: 1b2c3d4e5f6a7890으로 시작하는 디렉터리 찾기
ls ./dumpimage/1b2c3d4e5f6a7890*/

# layer.tar 내부 파일 목록 확인 (처음 10개만)
tar --list -f ./dumpimage/1b2c3d4e5f6a7890*/layer.tar | Select-Object -First 10

출력 예시:

hello.sh

분석:


# hello.sh 파일 내용 직접 출력 (압축 해제 없이)
tar -xOf ./dumpimage/1b2c3d4e5f6a7890*/layer.tar hello.sh

출력 예시:

#!/bin/bash
set -eu
echo "Hello, World!"
exec sleep infinity

분석:


5-3. 레이어 3 (chmod 실행) 확인

PowerShell (Windows):

# 레이어 3의 layer.tar 내용 확인
tar --list -f ./dumpimage/2c3d4e5f6a7b8901*/layer.tar

Bash (Linux/Mac):

# 레이어 3의 layer.tar 내용 확인
tar --list -f ./dumpimage/2c3d4e5f6a7b8901*/layer.tar

출력 예시:

hello.sh

분석:


6단계: 레이어 구조 시각화

위 분석 결과를 바탕으로 myimage:v1의 레이어 구조를 정리:

myimage:v1 레이어 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[레이어 3] 2c3d4e5f6a7b8901.../
│   layer.tar
│   └─ hello.sh (실행 권한 0755)
│   json
│   └─ RUN chmod +x /hello.sh
    ↓ 덮어쓰기

[레이어 2] 1b2c3d4e5f6a7890.../
│   layer.tar
│   └─ hello.sh (초기 권한 0644)
│   json
│   └─ COPY ./hello.sh /hello.sh
    ↓ 추가

[레이어 1] 0a1b2c3d4e5f6789.../
│   layer.tar
│   └─ /bin, /lib, /usr, /etc, ...
│   json
│   └─ FROM ubuntu:22.04
    ↓ 베이스

[최종 컨테이너 루트 파일시스템]
├─ /bin (레이어 1)
├─ /lib (레이어 1)
├─ /usr (레이어 1)
└─ /hello.sh (레이어 3, 실행 가능)

7단계: 정리

PowerShell (Windows):

# 실습 종료 후 정리 (선택)
# rm -r ./dumpimage

Bash (Linux/Mac):

# 실습 종료 후 정리 (선택)
# rm -rf ./dumpimage

실습 2: 오버레이 파일시스템 직접 체험

실습 목표

리눅스 overlayfs를 직접 마운트하여 레이어 중첩 및 Copy on Write(CoW) 동작을 확인

실습 환경


1단계: 리눅스 환경 진입 및 디렉터리 준비

Windows (WSL2 사용):

# PowerShell에서 WSL 진입
wsl

그 후 아래 Bash 명령어 실행:

# WSL 환경에서 실행
# Windows 드라이브의 실습 디렉터리로 이동 (본인 경로로 변경)
cd /mnt/c/docker-practice

# 오버레이 실습용 디렉터리 생성
mkdir -p overlay_test
cd overlay_test

Linux/Mac:

# 실습 디렉터리로 이동 (본인 경로로 변경)
cd ~/docker-practice

# 오버레이 실습용 디렉터리 생성
mkdir -p overlay_test
cd overlay_test

2단계: 레이어 디렉터리 생성 및 파일 준비

Linux 환경 (WSL2/Linux/Mac 공통):

# 필요한 디렉터리 생성
mkdir layer1 layer2 upper work merged

# layer2 (최하위 레이어 - ubuntu:22.04 역할)
echo "layer2: base file A" > ./layer2/a.txt
echo "layer2: base file B" > ./layer2/b.txt

# layer1 (중간 레이어 - hello.sh 추가 역할)
echo "layer1: modified A" > ./layer1/a.txt
echo "layer1: hello script" > ./layer1/hello.sh

# 생성된 파일 확인 (tree 명령어 사용)
tree .

# tree가 없는 경우 ls로 확인
# ls -la layer1/ layer2/

출력 예시:

.
├── layer1
│   ├── a.txt
│   └── hello.sh
├── layer2
│   ├── a.txt
│   └── b.txt
├── merged      (비어있음 - 마운트 포인트)
├── upper       (비어있음 - 쓰기 레이어)
└── work        (비어있음 - 커널 작업용)

레이어 구조 이해:

레이어 설계:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

layer1 (상위 레이어)
├─ a.txt        → "layer1: modified A"
└─ hello.sh     → "layer1: hello script"

layer2 (하위 레이어)
├─ a.txt        → "layer2: base file A"
└─ b.txt        → "layer2: base file B"

중첩 시 예상 결과:
├─ a.txt        → layer1의 a.txt (상위 우선)
├─ hello.sh     → layer1의 hello.sh
└─ b.txt        → layer2의 b.txt (layer1에 없음)

3단계: 오버레이 파일시스템 마운트

Linux 환경 (WSL2/Linux/Mac 공통):

# overlay 마운트 (루트 권한 필요)
sudo mount -t overlay overlay \
  -o lowerdir=layer1:layer2,upperdir=upper,workdir=work \
  merged

# 마운트 확인 (Linux/WSL2)
mount | grep overlay

💡 실습 가능 환경:

출력 예시 (WSL2/Linux):

overlay on /mnt/c/docker-practice/overlay_test/merged type overlay (rw,relatime,lowerdir=layer1:layer2,upperdir=upper,workdir=work)

명령어 구조 분석:

mount 명령어 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

sudo mount -t overlay overlay -o [...] merged
    │      │   │        │      │        │
    │      │   │        │      │        └─ 마운트 포인트 (결과가 보이는 곳)
    │      │   │        │      │
    │      │   │        │      └─ 옵션: 레이어 디렉터리 지정
    │      │   │        │         lowerdir: 읽기 전용 레이어 (여러 개 가능, ':' 구분)
    │      │   │        │         upperdir: 읽기/쓰기 레이어
    │      │   │        │         workdir: 커널 작업용 임시 디렉터리
    │      │   │        │
    │      │   │        └─ 장치 이름 (overlay 사용)
    │      │   │
    │      │   └─ 파일시스템 타입 (overlay)
    │      │
    │      └─ 마운트 명령어
    │
    └─ 루트 권한 실행

컨테이너 비유:
lowerdir = 이미지 레이어 (읽기 전용, 공유)
upperdir = 컨테이너 쓰기 레이어 (독립)
merged   = 컨테이너 루트 파일시스템

4단계: 레이어 중첩 결과 확인

# 마운트 포인트 내용 확인
tree ./merged

출력:

./merged
├── a.txt
├── b.txt
└── hello.sh
# 각 파일 내용 확인
cat ./merged/a.txt
cat ./merged/b.txt
cat ./merged/hello.sh

출력:

layer1: modified A      ← layer1의 a.txt (상위 우선)
layer2: base file B     ← layer2의 b.txt
layer1: hello script    ← layer1의 hello.sh

분석:

파일 우선순위 적용 결과:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

파일 이름    merged에서 보이는 버전      출처
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
a.txt       "layer1: modified A"      layer1 (상위 우선, layer2 가려짐)
hello.sh    "layer1: hello script"    layer1 (layer2에 없음)
b.txt       "layer2: base file B"     layer2 (layer1에 없음)

원칙:
→ 동일 파일명: 상위 레이어(lowerdir 앞쪽) 우선
→ 하위 레이어는 가려지지만 삭제되지 않음
→ 파일 중복 없이 투명한 병합

5단계: Copy on Write (CoW) 동작 확인

5-1. 쓰기 작업 전 upper 디렉터리 상태 확인

Linux 환경 (WSL2/Linux/Mac 공통):

# upper 디렉터리는 비어있어야 함 (아직 쓰기 작업 없음)
ls -la ./upper

출력:

total 0
drwxr-xr-x 2 user user 4096 Jan  8 10:00 .
drwxr-xr-x 7 user user 4096 Jan  8 09:58 ..

5-2. merged에서 파일 수정 (CoW 발동)

Linux 환경 (WSL2/Linux/Mac 공통):

# merged의 b.txt 수정 (원본은 layer2에 있음)
echo "Modified from merged!" > ./merged/b.txt

# 결과 확인
cat ./merged/b.txt

출력:

Modified from merged!
# upper 디렉터리 확인 (b.txt가 복사됨)
ls -la ./upper
cat ./upper/b.txt

출력:

total 4
drwxr-xr-x 2 user user 4096 Jan  8 10:05 .
drwxr-xr-x 7 user user 4096 Jan  8 09:58 ..
-rw-r--r-- 1 user user   23 Jan  8 10:05 b.txt   ← 복사됨
Modified from merged!   ← 변경된 내용

5-3. 원본 레이어 확인 (변경 안 됨)

Linux 환경 (WSL2/Linux/Mac 공통):

# layer2의 원본 b.txt는 변경되지 않음
cat ./layer2/b.txt

출력:

layer2: base file B   ← 원본 유지

CoW 동작 분석:

CoW 메커니즘 시각화:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[수정 전]
merged/b.txt 읽기 요청
    ↓
layer2/b.txt 발견 (읽기 전용)
    ↓
"layer2: base file B" 반환

upper: (비어있음)

[수정 시도]
merged/b.txt 쓰기 요청
    ↓
layer2는 읽기 전용 (쓰기 불가)
    ↓
CoW 발동: layer2/b.txt를 upper/로 복사
    ↓
upper/b.txt에 새 내용 작성

upper/b.txt: "Modified from merged!"
layer2/b.txt: "layer2: base file B" (변경 없음)

[이후 읽기]
merged/b.txt 읽기 요청
    ↓
upper/b.txt 발견 (최우선)
    ↓
"Modified from merged!" 반환

5-4. 새 파일 생성

Linux 환경 (WSL2/Linux/Mac 공통):

# merged에 새 파일 생성
echo "New file in container" > ./merged/new_file.txt

# 결과 확인
cat ./merged/new_file.txt
ls ./upper

출력:

New file in container

b.txt  new_file.txt   ← upper에 직접 생성됨

분석:


6단계: 레이어 공유 테스트 (새 컨테이너 시뮬레이션)

6-1. 새 마운트 포인트 생성

Linux 환경 (WSL2/Linux/Mac 공통):

# 동일한 lowerdir를 사용하는 새 컨테이너 시뮬레이션
mkdir new_upper new_work new_merged

sudo mount -t overlay overlay \
  -o lowerdir=layer1:layer2,upperdir=new_upper,workdir=new_work \
  new_merged

# 마운트 확인 (Linux/WSL2)
mount | grep overlay

출력:

overlay on .../merged type overlay (...)
overlay on .../new_merged type overlay (...)   ← 새로운 마운트

6-2. 레이어 공유 확인

Linux 환경 (WSL2/Linux/Mac 공통):

# new_merged도 동일한 layer1, layer2를 공유함
cat ./new_merged/a.txt
cat ./new_merged/b.txt

출력:

layer1: modified A      ← layer1 공유
layer2: base file B     ← layer2 공유 (원본 상태)

분석:


6-3. 독립적인 변경 확인

Linux 환경 (WSL2/Linux/Mac 공통):

# new_merged에서 파일 수정
echo "New container modification" > ./new_merged/a.txt

# 각 컨테이너에서 확인
cat ./new_merged/a.txt
cat ./merged/a.txt

출력:

New container modification   ← new_merged의 변경사항
layer1: modified A           ← merged는 영향 없음
# upper 디렉터리 비교
ls ./upper
ls ./new_upper

출력:

b.txt  new_file.txt         ← 첫 번째 컨테이너
a.txt                        ← 두 번째 컨테이너

레이어 공유 메커니즘 시각화:

레이어 공유와 독립성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[공유 읽기 전용 레이어]
layer1/ ─┐
layer2/ ─┤
         ├──→ [컨테이너 1]
         │    merged/
         │    upper/ (b.txt, new_file.txt)
         │
         └──→ [컨테이너 2]
              new_merged/
              new_upper/ (a.txt)

특징:
→ layer1, layer2는 공유 (메모리/스토리지 절약)
→ upper는 독립 (각 컨테이너 전용)
→ 한 컨테이너의 변경이 다른 컨테이너에 영향 없음
→ 동일 이미지로 수백 개 컨테이너 실행해도 효율적

7단계: 실습 정리

Linux 환경 (WSL2/Linux/Mac 공통):

# 마운트 해제 (중요!)
sudo umount ./merged
sudo umount ./new_merged

# 마운트 해제 확인
mount | grep overlay
# (출력 없음)

# 실습 디렉터리 삭제 (선택)
cd ..
rm -rf overlay_test

Windows 사용자 (WSL 종료):

# WSL 종료
exit

그 후 PowerShell로 돌아옴


실습 3: Docker 스토리지 위치 확인 (선택)

실습 목표

실제 Docker가 레이어를 저장하는 /var/lib/docker/overlay2 디렉터리를 확인하여 실습 2와의 연결성을 이해

용어 정리

GraphDriver: 도커 이미지/컨테이너의 스토리지 관련 정보. 레이어 경로, 마운트 정보 등 포함.

nsenter: 다른 프로세스의 네임스페이스에 진입하는 명령어. 컨테이너/VM 내부 접근에 사용됨.

실습 환경


사전 준비: Windows 사용자 - Docker Desktop WSL2 통합 확인

Windows 사용자는 먼저 다음을 확인해주세요:

⚠️ WSL2에서 Docker 명령어가 작동하지 않는 경우:

Docker Desktop에서 WSL2 통합을 활성화해야 합니다.

설정 방법:

  1. Docker Desktop 실행
  2. 설정(Settings) → Resources → WSL Integration
  3. "Enable integration with my default WSL distro" 체크
  4. 사용 중인 Ubuntu 배포판 토글 활성화
  5. "Apply & Restart" 클릭

설정 후 WSL을 재시작하거나 새 터미널을 열어주세요.

또는 PowerShell에서 직접 실행하는 방법:

# PowerShell에서 직접 Docker 명령 실행 (WSL 진입 없이)
docker info | Select-String "Storage Driver" -Context 0,5

💡 Mac 사용자: Docker Desktop은 별도 VM에서 실행되므로 직접 접근이 어려움. 이 실습은 건너뛰어도 무방


1단계: Docker 스토리지 드라이버 확인

방법 1: PowerShell에서 직접 실행 (Windows, 권장):

# Docker 정보 확인 (WSL 진입 불필요)
docker info | Select-String "Storage Driver" -Context 0,5

방법 2: WSL2 환경에서 실행 (Linux):

# PowerShell에서 WSL 진입 (Windows만 해당)
wsl

Windows PowerShell:

# Docker 정보 확인
docker info | Select-String "Storage Driver" -Context 0,5

Linux/Mac (Bash) 환경:

# Docker 정보 확인
docker info | grep -A 5 "Storage Driver"

Windows (Docker Desktop) 출력 예시:

Storage Driver: overlayfs
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true

Linux (Native Docker) 출력 예시:

Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true

💡 스토리지 드라이버 차이:


2단계: overlay2 디렉터리 확인

⚠️ Windows + Docker Desktop 사용자 주의:

Docker Desktop을 사용하는 경우, /var/lib/docker는 Docker Desktop의 VM 내부에 위치

중요: Docker Desktop은 overlay2 디렉터리를 사용하지 않음. overlayfs를 사용하며, VM 내부 확인 시 /var/lib/docker 아래에 다음 구조 존재:

결론: 이 단계(overlay2 확인)는 Windows Docker Desktop에서 불가능. 바로 3단계(GraphDriver 정보 확인)로 진행

참고: VM 내부 접근 방법 (overlay2는 없지만 구조 확인 가능):

docker run -it --rm --privileged --pid=host alpine nsenter -t 1 -m -u -n -i sh
ls -la /var/lib/docker/
exit

Linux 환경 (Native Docker):

# overlay2 저장소 위치 확인
sudo ls -la /var/lib/docker/overlay2 | head -n 20

출력 예시:

drwx--x---  3 root root 4096 Jan  8 09:00 0a1b2c3d4e5f6789...
drwx--x---  3 root root 4096 Jan  8 09:00 1b2c3d4e5f6a7890...
drwx--x---  3 root root 4096 Jan  8 10:00 2c3d4e5f6a7b8901...
drwx--x---  3 root root 4096 Jan  8 10:05 3d4e5f6a7b8c9012...
...

분석:


3단계: myimage:v1의 레이어 찾기

PowerShell (Windows, 권장):

# myimage:v1의 GraphDriver 정보 확인
docker inspect myimage:v1 | Select-String "GraphDriver" -Context 0,15

Windows (Docker Desktop) 출력 예시:

"GraphDriver": {
    "Data": null,
    "Name": "overlayfs"
},
"RootFS": {
    "Type": "layers",
    "Layers": [
        "sha256:73974f74b436...",
        "sha256:36a90ecdccdf...",
        "sha256:5f70bf18a086..."
    ]
}

Windows PowerShell:

# myimage:v1의 GraphDriver 정보 확인
docker inspect myimage:v1 | Select-String "GraphDriver" -Context 0,15

Linux/Mac (Bash) 환경:

# myimage:v1의 GraphDriver 정보 확인
docker inspect myimage:v1 | grep -A 15 "GraphDriver"

Linux (Native Docker) 출력 예시:

"GraphDriver": {
    "Data": {
        "LowerDir": "/var/lib/docker/overlay2/1b2c3d4e5f6a7890.../diff:
                     /var/lib/docker/overlay2/0a1b2c3d4e5f6789.../diff",
        "MergedDir": "/var/lib/docker/overlay2/2c3d4e5f6a7b8901.../merged",
        "UpperDir": "/var/lib/docker/overlay2/2c3d4e5f6a7b8901.../diff",
        "WorkDir": "/var/lib/docker/overlay2/2c3d4e5f6a7b8901.../work"
    },
    "Name": "overlay2"
}

분석:

Windows (Docker Desktop):

Linux (Native Docker):

용어 정리

LowerDir / UpperDir / WorkDir: LowerDir는 읽기 전용 하위 레이어들, UpperDir는 읽기/쓰기 가능한 상위 레이어, WorkDir는 커널이 내부 작업에 사용하는 디렉터리.

MergedDir: 모든 레이어가 중첩된 최종 결과. 컨테이너가 실제로 보는 파일시스템.

💡 Windows 사용자: Docker Desktop은 추상화 계층으로 인해 실제 파일 시스템 경로가 보이지 않음. 하지만 RootFS.Layers에서 각 레이어의 해시를 확인할 수 있으며, 레이어 개념 이해에는 충분


4단계: 특정 레이어 내용 확인

💡 참고: 이 단계는 Linux Native Docker 환경에서만 가능. Windows Docker Desktop 사용자는 이 단계를 건너뛰고 5단계로 진행

Linux 환경 (Native Docker만 해당):

# 레이어 디렉터리 내용 확인 (UpperDir 경로 사용)
sudo ls -la /var/lib/docker/overlay2/2c3d4e5f6a7b8901.../diff

출력 예시:

drwxr-xr-x  2 root root 4096 Jan  8 10:00 .
drwx--x---  5 root root 4096 Jan  8 10:00 ..
-rwxr-xr-x  1 root root   65 Jan  8 10:00 hello.sh   ← 레이어 내용
# hello.sh 내용 확인
sudo cat /var/lib/docker/overlay2/2c3d4e5f6a7b8901.../diff/hello.sh

출력:

#!/bin/bash
set -eu
echo "Hello, World!"
exec sleep infinity

분석:

Windows 사용자를 위한 대안:

실제 레이어 파일을 직접 볼 수는 없지만, 3단계에서 확인한 RootFS.Layers의 SHA256 해시가 각 레이어를 고유하게 식별:


5단계: 실행 중인 컨테이너의 레이어 확인

PowerShell (Windows):

# 컨테이너 실행
docker run -d --name layer_test myimage:v1

# 컨테이너의 GraphDriver 확인
docker inspect layer_test | Select-String "GraphDriver" -Context 0,15

Windows (Docker Desktop) 출력 예시:

"GraphDriver": {
    "Data": null,
    "Name": "overlayfs"
}

분석 (Windows):

Linux 환경 (Native Docker):

Windows PowerShell:

# 컨테이너 실행
docker run -d --name layer_test myimage:v1

# 컨테이너의 GraphDriver 확인
docker inspect layer_test | Select-String "GraphDriver" -Context 0,15

Linux/Mac (Bash):

# 컨테이너 실행
docker run -d --name layer_test myimage:v1

# 컨테이너의 GraphDriver 확인
docker inspect layer_test | grep -A 15 "GraphDriver"

Linux 출력 예시:

"GraphDriver": {
    "Data": {
        "LowerDir": "/var/lib/docker/overlay2/3d4e5f6a7b8c9012.../diff:
                     /var/lib/docker/overlay2/2c3d4e5f6a7b8901.../diff:
                     /var/lib/docker/overlay2/1b2c3d4e5f6a7890.../diff:
                     /var/lib/docker/overlay2/0a1b2c3d4e5f6789.../diff",
        "MergedDir": "/var/lib/docker/overlay2/3d4e5f6a7b8c9012.../merged",
        "UpperDir": "/var/lib/docker/overlay2/3d4e5f6a7b8c9012.../diff",
        "WorkDir": "/var/lib/docker/overlay2/3d4e5f6a7b8c9012.../work"
    },
    "Name": "overlay2"
}

분석 (Linux):


컨테이너 쓰기 레이어 테스트 (모든 OS 공통):

# PowerShell 또는
# Bash

공통 명령어:

# 컨테이너 내부에서 파일 생성
docker exec layer_test bash -c "echo 'Container data' > /test.txt"

# 컨테이너 내부에서 확인
docker exec layer_test cat /test.txt

출력:

Container data   ← 컨테이너 쓰기 레이어에 저장됨

💡 핵심 개념:


정리:

모든 OS 공통:

# 컨테이너 정리
docker stop layer_test
docker rm layer_test

실습 종합 정리

3가지 실습의 연결성

레이어 구조 이해의 흐름:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[실습 1] docker save로 이미지 분석
    ↓
이미지는 여러 layer.tar의 집합
각 tar는 파일시스템 변경사항 포함
    ↓
[실습 2] overlayfs 직접 체험
    ↓
여러 디렉터리를 중첩하면 하나의 FS로 보임
상위 레이어 우선, CoW로 하위 보호
    ↓
[실습 3] Docker 실제 저장소 확인
    ↓
Docker는 /var/lib/docker/overlay2에 레이어 저장
실행 시 overlayfs로 마운트
컨테이너는 독립적인 upper 레이어 생성

핵심 개념 재확인

1. 레이어는 tar 아카이브

2. overlayfs로 레이어 중첩

3. Copy on Write (CoW)

4. 레이어 공유

5. 독립적인 컨테이너 파일시스템


추가 학습 자료

관련 명령어

Windows PowerShell:

# 이미지 히스토리 (레이어별 크기 확인)
docker history myimage:v1

# 이미지 상세 정보
docker inspect myimage:v1

# 컨테이너 변경사항 확인
docker diff [컨테이너명]

# 현재 사용 중인 스토리지 드라이버
docker info | Select-String "Storage Driver"

Linux/Mac (Bash):

# 이미지 히스토리 (레이어별 크기 확인)
docker history myimage:v1

# 이미지 상세 정보
docker inspect myimage:v1

# 컨테이너 변경사항 확인
docker diff [컨테이너명]

# 현재 사용 중인 스토리지 드라이버
docker info | grep "Storage Driver"
용어 정리

docker diff: 컨테이너에서 변경된 파일 목록 확인. A(추가), C(변경), D(삭제)로 표시됨.

docker history: 이미지의 레이어 히스토리 확인. 각 레이어의 크기, 생성 명령어 표시.

overlay2 vs overlay 차이

항목 overlay overlay2
도입 시기 Docker 1.12 Docker 17.06+
inode 사용 높음 (하드링크 많음) 낮음 (효율적)
성능 중간 높음
권장 여부 레거시 ✅ 권장
커널 요구사항 3.18+ 4.0+
용어 정리

inode: 파일시스템에서 파일 메타데이터를 저장하는 자료구조. 파일 권한, 크기, 위치 정보 포함.

실무 팁

1. 레이어 수 최소화

# 나쁜 예
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2

# 좋은 예
RUN apt-get update && apt-get install -y \
    package1 \
    package2 \
    && rm -rf /var/lib/apt/lists/*

2. .dockerignore 사용

3. 멀티 스테이지 빌드


참고 자료

공식 문서:

도구: